/***************************************************************************/
/*                                                                         */
/*  psmodule.c                                                             */
/*                                                                         */
/*    PSNames module implementation (body).                                */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2005, 2006, 2007, 2008 by             */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#include <ft2build.h>
#include FT_INTERNAL_OBJECTS_H
#include FT_SERVICE_POSTSCRIPT_CMAPS_H

#include "psmodule.h"
#include "pstables.h"

#include "psnamerr.h"
#include "pspic.h"


#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES


    #ifdef FT_CONFIG_OPTION_ADOBE_GLYPH_LIST


        #define VARIANT_BIT 0x80000000UL
        #define BASE_GLYPH(code)  ((FT_UInt32)((code) & ~VARIANT_BIT))


/* Return the Unicode value corresponding to a given glyph.  Note that */
/* we do deal with glyph variants by detecting a non-initial dot in    */
/* the name, as in `A.swash' or `e.final'; in this case, the           */
/* VARIANT_BIT is set in the return value.                             */
/*                                                                     */
static FT_UInt32
ps_unicode_value(const char* glyph_name)
{
    /* If the name begins with `uni', then the glyph name may be a */
    /* hard-coded unicode character code.                          */
    if (glyph_name[0] == 'u' &&
        glyph_name[1] == 'n' &&
        glyph_name[2] == 'i')
    {
        /* determine whether the next four characters following are */
        /* hexadecimal.                                             */

        /* XXX: Add code to deal with ligatures, i.e. glyph names like */
        /*      `uniXXXXYYYYZZZZ'...                                   */

        FT_Int count;
        FT_UInt32 value = 0;
        const char* p = glyph_name + 3;


        for (count = 4; count > 0; count--, p++)
        {
            char c = *p;
            unsigned int d;


            d = (unsigned char)c - '0';
            if (d >= 10)
            {
                d = (unsigned char)c - 'A';
                if (d >= 6)
                    d = 16;
                else
                    d += 10;
            }

            /* Exit if a non-uppercase hexadecimal character was found   */
            /* -- this also catches character codes below `0' since such */
            /* negative numbers cast to `unsigned int' are far too big.  */
            if (d >= 16)
                break;

            value = (value << 4) + d;
        }

        /* there must be exactly four hex digits */
        if (count == 0)
        {
            if (*p == '\0')
                return value;
            if (*p == '.')
                return (FT_UInt32)(value | VARIANT_BIT);
        }
    }

    /* If the name begins with `u', followed by four to six uppercase */
    /* hexadecimal digits, it is a hard-coded unicode character code. */
    if (glyph_name[0] == 'u')
    {
        FT_Int count;
        FT_UInt32 value = 0;
        const char* p = glyph_name + 1;


        for (count = 6; count > 0; count--, p++)
        {
            char c = *p;
            unsigned int d;


            d = (unsigned char)c - '0';
            if (d >= 10)
            {
                d = (unsigned char)c - 'A';
                if (d >= 6)
                    d = 16;
                else
                    d += 10;
            }

            if (d >= 16)
                break;

            value = (value << 4) + d;
        }

        if (count <= 2)
        {
            if (*p == '\0')
                return value;
            if (*p == '.')
                return (FT_UInt32)(value | VARIANT_BIT);
        }
    }

    /* Look for a non-initial dot in the glyph name in order to */
    /* find variants like `A.swash', `e.final', etc.            */
    {
        const char* p = glyph_name;
        const char* dot = NULL;


        for (; *p; p++)
        {
            if (*p == '.' && p > glyph_name)
            {
                dot = p;
                break;
            }
        }

        /* now look up the glyph in the Adobe Glyph List */
        if (!dot)
            return (FT_UInt32)ft_get_adobe_glyph_index(glyph_name, p);
        else
            return (FT_UInt32)(ft_get_adobe_glyph_index(glyph_name, dot) |
                               VARIANT_BIT);
    }
}


/* ft_qsort callback to sort the unicode map */
FT_CALLBACK_DEF(int)
compare_uni_maps(const void* a,
                 const void* b)
{
    PS_UniMap* map1 = (PS_UniMap*)a;
    PS_UniMap* map2 = (PS_UniMap*)b;
    FT_UInt32 unicode1 = BASE_GLYPH(map1->unicode);
    FT_UInt32 unicode2 = BASE_GLYPH(map2->unicode);


    /* sort base glyphs before glyph variants */
    if (unicode1 == unicode2)
    {
        if (map1->unicode > map2->unicode)
            return 1;
        else if (map1->unicode < map2->unicode)
            return -1;
        else
            return 0;
    }
    else
    {
        if (unicode1 > unicode2)
            return 1;
        else if (unicode1 < unicode2)
            return -1;
        else
            return 0;
    }
}


/* support for extra glyphs not handled (well) in AGL; */
/* we add extra mappings for them if necessary         */

        #define EXTRA_GLYPH_LIST_SIZE 10

static const FT_UInt32 ft_extra_glyph_unicodes[EXTRA_GLYPH_LIST_SIZE] =
{
    /* WGL 4 */
    0x0394,
    0x03A9,
    0x2215,
    0x00AD,
    0x02C9,
    0x03BC,
    0x2219,
    0x00A0,
    /* Romanian */
    0x021A,
    0x021B
};

static const char ft_extra_glyph_names[] =
{
    'D', 'e', 'l', 't', 'a', 0,
    'O', 'm', 'e', 'g', 'a', 0,
    'f', 'r', 'a', 'c', 't', 'i', 'o', 'n', 0,
    'h', 'y', 'p', 'h', 'e', 'n', 0,
    'm', 'a', 'c', 'r', 'o', 'n', 0,
    'm', 'u', 0,
    'p', 'e', 'r', 'i', 'o', 'd', 'c', 'e', 'n', 't', 'e', 'r', 'e', 'd', 0,
    's', 'p', 'a', 'c', 'e', 0,
    'T', 'c', 'o', 'm', 'm', 'a', 'a', 'c', 'c', 'e', 'n', 't', 0,
    't', 'c', 'o', 'm', 'm', 'a', 'a', 'c', 'c', 'e', 'n', 't', 0
};

static const FT_Int
ft_extra_glyph_name_offsets[EXTRA_GLYPH_LIST_SIZE] =
{
    0,
    6,
    12,
    21,
    28,
    35,
    38,
    53,
    59,
    72
};


static void
ps_check_extra_glyph_name(const char* gname,
                          FT_UInt glyph,
                          FT_UInt* extra_glyphs,
                          FT_UInt* states)
{
    FT_UInt n;


    for (n = 0; n < EXTRA_GLYPH_LIST_SIZE; n++)
    {
        if (ft_strcmp(ft_extra_glyph_names +
                      ft_extra_glyph_name_offsets[n], gname) == 0)
        {
            if (states[n] == 0)
            {
                /* mark this extra glyph as a candidate for the cmap */
                states[n] = 1;
                extra_glyphs[n] = glyph;
            }

            return;
        }
    }
}


static void
ps_check_extra_glyph_unicode(FT_UInt32 uni_char,
                             FT_UInt* states)
{
    FT_UInt n;


    for (n = 0; n < EXTRA_GLYPH_LIST_SIZE; n++)
    {
        if (uni_char == ft_extra_glyph_unicodes[n])
        {
            /* disable this extra glyph from being added to the cmap */
            states[n] = 2;

            return;
        }
    }
}


/* Build a table that maps Unicode values to glyph indices. */
static FT_Error
ps_unicodes_init(FT_Memory memory,
                 PS_Unicodes table,
                 FT_UInt num_glyphs,
                 PS_GetGlyphNameFunc get_glyph_name,
                 PS_FreeGlyphNameFunc free_glyph_name,
                 FT_Pointer glyph_data)
{
    FT_Error error;

    FT_UInt extra_glyph_list_states[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    FT_UInt extra_glyphs[EXTRA_GLYPH_LIST_SIZE];


    /* we first allocate the table */
    table->num_maps = 0;
    table->maps = 0;

    if (!FT_NEW_ARRAY(table->maps, num_glyphs + EXTRA_GLYPH_LIST_SIZE))
    {
        FT_UInt n;
        FT_UInt count;
        PS_UniMap* map;
        FT_UInt32 uni_char;


        map = table->maps;

        for (n = 0; n < num_glyphs; n++)
        {
            const char* gname = get_glyph_name(glyph_data, n);


            if (gname)
            {
                ps_check_extra_glyph_name(gname, n,
                                          extra_glyphs, extra_glyph_list_states);
                uni_char = ps_unicode_value(gname);

                if (BASE_GLYPH(uni_char) != 0)
                {
                    ps_check_extra_glyph_unicode(uni_char,
                                                 extra_glyph_list_states);
                    map->unicode = uni_char;
                    map->glyph_index = n;
                    map++;
                }

                if (free_glyph_name)
                    free_glyph_name(glyph_data, gname);
            }
        }

        for (n = 0; n < EXTRA_GLYPH_LIST_SIZE; n++)
        {
            if (extra_glyph_list_states[n] == 1)
            {
                /* This glyph name has an additional representation. */
                /* Add it to the cmap.                               */

                map->unicode = ft_extra_glyph_unicodes[n];
                map->glyph_index = extra_glyphs[n];
                map++;
            }
        }

        /* now compress the table a bit */
        count = (FT_UInt)(map - table->maps);

        if (count == 0)
        {
            FT_FREE(table->maps);
            if (!error)
                error = PSnames_Err_Invalid_Argument; /* No unicode chars here! */
        }
        else
        {
            /* Reallocate if the number of used entries is much smaller. */
            if (count < num_glyphs / 2)
            {
                FT_RENEW_ARRAY(table->maps, num_glyphs, count);
                error = PSnames_Err_Ok;
            }

            /* Sort the table in increasing order of unicode values, */
            /* taking care of glyph variants.                        */
            ft_qsort(table->maps, count, sizeof(PS_UniMap),
                     compare_uni_maps);
        }

        table->num_maps = count;
    }

    return error;
}


static FT_UInt
ps_unicodes_char_index(PS_Unicodes table,
                       FT_UInt32 unicode)
{
    PS_UniMap* min, * max, * mid, * result = NULL;


    /* Perform a binary search on the table. */

    min = table->maps;
    max = min + table->num_maps - 1;

    while (min <= max)
    {
        FT_UInt32 base_glyph;


        mid = min + ((max - min) >> 1);

        if (mid->unicode == unicode)
        {
            result = mid;
            break;
        }

        base_glyph = BASE_GLYPH(mid->unicode);

        if (base_glyph == unicode)
            result = mid; /* remember match but continue search for base glyph */

        if (min == max)
            break;

        if (base_glyph < unicode)
            min = mid + 1;
        else
            max = mid - 1;
    }

    if (result)
        return result->glyph_index;
    else
        return 0;
}


static FT_UInt32
ps_unicodes_char_next(PS_Unicodes table,
                      FT_UInt32* unicode)
{
    FT_UInt result = 0;
    FT_UInt32 char_code = *unicode + 1;


    {
        FT_UInt min = 0;
        FT_UInt max = table->num_maps;
        FT_UInt mid;
        PS_UniMap* map;
        FT_UInt32 base_glyph;


        while (min < max)
        {
            mid = min + ((max - min) >> 1);
            map = table->maps + mid;

            if (map->unicode == char_code)
            {
                result = map->glyph_index;
                goto Exit;
            }

            base_glyph = BASE_GLYPH(map->unicode);

            if (base_glyph == char_code)
                result = map->glyph_index;

            if (base_glyph < char_code)
                min = mid + 1;
            else
                max = mid;
        }

        if (result)
            goto Exit;           /* we have a variant glyph */

        /* we didn't find it; check whether we have a map just above it */
        char_code = 0;

        if (min < table->num_maps)
        {
            map = table->maps + min;
            result = map->glyph_index;
            char_code = BASE_GLYPH(map->unicode);
        }
    }

Exit:
    *unicode = char_code;
    return result;
}


    #endif /* FT_CONFIG_OPTION_ADOBE_GLYPH_LIST */


static const char*
ps_get_macintosh_name(FT_UInt name_index)
{
    if (name_index >= FT_NUM_MAC_NAMES)
        name_index = 0;

    return ft_standard_glyph_names + ft_mac_names[name_index];
}


static const char*
ps_get_standard_strings(FT_UInt sid)
{
    if (sid >= FT_NUM_SID_NAMES)
        return 0;

    return ft_standard_glyph_names + ft_sid_names[sid];
}


    #ifdef FT_CONFIG_OPTION_ADOBE_GLYPH_LIST
FT_DEFINE_SERVICE_PSCMAPSREC(pscmaps_interface,
                             (PS_Unicode_ValueFunc)ps_unicode_value,
                             (PS_Unicodes_InitFunc)ps_unicodes_init,
                             (PS_Unicodes_CharIndexFunc)ps_unicodes_char_index,
                             (PS_Unicodes_CharNextFunc)ps_unicodes_char_next,

                             (PS_Macintosh_NameFunc)ps_get_macintosh_name,
                             (PS_Adobe_Std_StringsFunc)ps_get_standard_strings,

                             t1_standard_encoding,
                             t1_expert_encoding
                             )

    #else

FT_DEFINE_SERVICE_PSCMAPSREC(pscmaps_interface,
                             0,
                             0,
                             0,
                             0,

                             (PS_Macintosh_NameFunc)ps_get_macintosh_name,
                             (PS_Adobe_Std_StringsFunc)ps_get_standard_strings,

                             t1_standard_encoding,
                             t1_expert_encoding
                             )

    #endif /* FT_CONFIG_OPTION_ADOBE_GLYPH_LIST */


FT_DEFINE_SERVICEDESCREC1(pscmaps_services,
                          FT_SERVICE_ID_POSTSCRIPT_CMAPS, &FT_PSCMAPS_INTERFACE_GET
                          )




static FT_Pointer
psnames_get_service(FT_Module module,
                    const char* service_id)
{
    FT_UNUSED(module);

    return ft_service_list_lookup(FT_PSCMAPS_SERVICES_GET, service_id);
}

#endif /* FT_CONFIG_OPTION_POSTSCRIPT_NAMES */


#ifndef FT_CONFIG_OPTION_POSTSCRIPT_NAMES
    #define PUT_PS_NAMES_SERVICE(a) 0
#else
    #define PUT_PS_NAMES_SERVICE(a) a
#endif

FT_DEFINE_MODULE(psnames_module_class,

                 0, /* this is not a font driver, nor a renderer */
                 sizeof(FT_ModuleRec),

                 "psnames", /* driver name                         */
                 0x10000L, /* driver version                      */
                 0x20000L, /* driver requires FreeType 2 or above */

                 PUT_PS_NAMES_SERVICE((void*)&FT_PSCMAPS_INTERFACE_GET), /* module specific interface */
                 (FT_Module_Constructor)0,
                 (FT_Module_Destructor)0,
                 (FT_Module_Requester)PUT_PS_NAMES_SERVICE(psnames_get_service)
                 )



/* END */